<!-- src/layouts/SidebarLayout.vue -->
<template>
  <el-menu
    class="el-menu-vertical-demo"
    :default-active="activePath"
    :default-openeds="openKeys"
    :collapse="isCollapsed"
    router
    background-color="#ffffff"
    text-color="#767c82"
    active-text-color="#0084F4"
    unique-opened
    :collapse-transition="false"
    @select="onSelect"
    @open="onOpen"
    @close="onClose"
  >
    <!-- 一级菜单循环 -->
    <template v-for="menu in menuTree" :key="menu.path">
      <!-- 有子菜单（无论收缩还是展开都用 el-sub-menu） -->
      <el-sub-menu
        v-if="menu.children && menu.children.length"
        :index="menu.path"
        :class="{ 'parent-active': activeParent === menu.path }"
      >
        <template #title>
          <i
            :class="['iconfont', menu.meta.icon || 'icon-a-ziyuan878']"
            :style="{ color: activeParent === menu.path ? '#0084f4' : '' }"
          ></i>
          <span class="menu-text" :style="{ color: activeParent === menu.path ? '#0084f4' : '' }">
            {{ menu.meta.title }}
          </span>
        </template>

        <!-- 二级菜单 -->
        <el-menu-item v-for="sub in menu.children" :key="sub.path" :index="sub.path">
          <span class="menu-text" :style="{ color: activePath === sub.path ? '#0084f4' : '' }">
            {{ sub.meta.title }}
          </span>
        </el-menu-item>
      </el-sub-menu>

      <!-- 无子菜单，普通一级项 -->
      <el-menu-item v-else :index="menu.path">
        <i :class="['iconfont', menu.meta.icon || 'icon-a-ziyuan878']"></i>
        <span class="menu-text">{{ menu.meta.title }}</span>
      </el-menu-item>
    </template>
  </el-menu>
</template>

<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { useRouter, useRoute, type RouteRecordRaw } from 'vue-router'

const props = defineProps<{ isCollapsed?: boolean }>()
const emit = defineEmits<{ (e: 'changeMenu', title: string): void }>()

const isCollapsed = computed(() => props.isCollapsed ?? false)

const vueRouter = useRouter()
const route = useRoute()

function joinPath(parent: string, child: string) {
  const p = parent === '/' ? '' : parent || ''
  if (!child || child === '') return p || '/'
  if (child.startsWith('/')) return child
  return `${p}/${child}`.replace(/\/+/g, '/')
}

type MenuNode = {
  path: string
  meta: Exclude<RouteRecordRaw['meta'], undefined>
  children?: MenuNode[]
}

function buildMenuTree(routes: RouteRecordRaw[], base = ''): MenuNode[] {
  return routes
    .filter(r => r.meta?.isMenu)
    .map(r => {
      const full = joinPath(base, r.path)
      const children = r.children ? buildMenuTree(r.children, full) : undefined
      return {
        path: full,
        meta: r.meta!,
        children: children && children.length ? children : undefined
      }
    })
}

const menuTree = computed<MenuNode[]>(() => {
  const root = (vueRouter.options.routes || []).find(r => r.path === '/')
  if (!root || !root.children) {
    const all = (vueRouter.options.routes || []) as RouteRecordRaw[]
    return buildMenuTree(all, '')
  }
  return buildMenuTree(root.children as RouteRecordRaw[], '/')
})

const activePath = ref<string>(route.path || '/')
const activeParent = ref<string | null>(null)
const openKeys = ref<string[]>([])
const lastOpenKeys = ref<string[] | null>(null)

watch(
  () => route.path,
  p => {
    activePath.value = p || '/'
    const parent = menuTree.value.find(m => m.children?.some(c => c.path === activePath.value))
    if (parent) {
      activeParent.value = parent.path
      openKeys.value = [parent.path]
    } else {
      activeParent.value = null
      openKeys.value = []
    }
  },
  { immediate: true }
)

watch(
  isCollapsed,
  collapsed => {
    if (collapsed) {
      lastOpenKeys.value = openKeys.value.slice()
      openKeys.value = []
    } else {
      openKeys.value = lastOpenKeys.value ?? openKeys.value
      lastOpenKeys.value = null
    }
  },
  { immediate: true }
)

const onOpen = (index: string) => {
  openKeys.value = [index]
}
const onClose = (index: string) => {
  openKeys.value = openKeys.value.filter(k => k !== index)
}
const onSelect = (index: string) => {
  const findNode = (nodes: MenuNode[] = [], idx: string): MenuNode | undefined => {
    for (const n of nodes) {
      if (n.path === idx) return n
      if (n.children) {
        const f = findNode(n.children, idx)
        if (f) return f
      }
    }
    return undefined
  }

  const found = findNode(menuTree.value, index)
  if (found) {
    emit('changeMenu', (found.meta.title as string) || '')
  }

  if (index) {
    vueRouter.push(index).catch(() => {})
  }
}
</script>

<style scoped>
.el-menu-vertical-demo {
  height: 100%;
  border-right: 1px solid #eaeaea;
  background: transparent;
}
.menu-text {
  margin-left: 8px;
  transition:
    max-width 0.22s cubic-bezier(0.4, 0, 0.2, 1),
    opacity 0.18s linear,
    color 120ms linear;
  display: inline-block;
  white-space: nowrap;
  overflow: hidden;
  max-width: 180px;
  vertical-align: middle;
}
:deep(.el-menu--collapse) .menu-text {
  max-width: 0;
  opacity: 0;
}
/* 收缩态隐藏箭头 */
:deep(.el-menu--collapse) .el-sub-menu__title .el-sub-menu__icon-arrow {
  display: none !important;
}
/* 父级高亮 */
:deep(.parent-active) > :deep(.el-sub-menu__title) {
  background-color: rgba(0, 132, 244, 0.06);
}
:deep(.parent-active) > :deep(.el-sub-menu__title) .iconfont,
:deep(.parent-active) > :deep(.el-sub-menu__title) .menu-text {
  color: #0084f4 !important;
  font-weight: 600;
}
:deep(.el-menu-item.is-active) .menu-text {
  color: #0084f4 !important;
  font-weight: 600;
}
</style>
